#ifndef _UNIT_H_
#define _UNIT_H_

#include <stdlib.h>
#include <list>
#include <vector>
#include "Script.h"
#include "JackClient.h"

#define UNCONNECTED NULL

#define OPTION_ADD 1

class Options
{
   public:
      unsigned isAddition : 1;
      inline Options& setAddition(bool v) { isAddition = v; return *this; }

      unsigned isDebugBuffer : 1;
      inline Options& setIsDebugBuffer(bool v) { isDebugBuffer = v; return *this; }

      Options()
      {
         isAddition = false;
         isDebugBuffer = false;
      }
};

class Unit;

/* Port objects are not really used in internal objects communication and calculations.
 * It's for GUI and introspection.
 */
enum PortType
{
   AUDIO,
   DATA
};
enum PortIOType
{
   INPUT,
   OUTPUT
};
class Port
{
   private:
      string mName;
      PortType mType;
      PortIOType mIoType;

   public:
      Port(string name, PortType type, PortIOType iotype)
      {
         mName = name;
         mType = type;
         mIoType = iotype;
      }

      string getName() { return mName; }
      PortType getType() { return mType; }
};

class Connection
{
   private:
      Unit *mUnit;
      unsigned mSrcPort;
      unsigned mDstPort;

   public:
      Connection(unsigned srcPort, Unit *unit, unsigned dstPort)
      {
         mUnit = unit;
         mSrcPort = srcPort;
         mDstPort = dstPort;
      }
      Unit* getUnit() { return mUnit; }
      unsigned getSrcPort() { return mSrcPort; }
      unsigned getDstPort() { return mDstPort; }
};

class Unit
{
   protected:
      vector< list<Connection*>* > mAudioIn;
      vector< list<Connection*>* > mDataOut;
      list<Port*> mPorts;
      string mName;
      int mScTypeTag;

   public:
      Unit();
      ~Unit();

      void createPorts(unsigned audioPorts, unsigned dataPorts);
      void readFromPort(unsigned port, sample_t *buffer, jack_nframes_t len, Options options);

      virtual void ar(unsigned portNum, sample_t *buffer, jack_nframes_t len, Options options) = 0;
      virtual Unit& ctl(unsigned port, Message msg);

      void setName(string name) { mName = name; }
      string getName() { return mName; }

      void addPort(string name, PortType type, PortIOType iotype);

      unsigned getAudioPortCount() { return mAudioIn.size(); }
      unsigned getDataPortCount() { return mDataOut.size(); }

      void setScTypeTag(int t) { mScTypeTag = t; }
      int getScTypeTag() { return mScTypeTag; }

      friend void connect(Unit *srcUnit, Unit *dstUnit, unsigned srcPort = 0, unsigned dstPort = 0);
      friend void connect(Unit &srcUnit, Unit &dstUnit, unsigned srcPort = 0, unsigned dstPort = 0);

      list<Connection*>* getConnections(unsigned port);
      list<Port*> getPorts() { return mPorts; }
};

class Dac : public Unit
{
   public:
      Dac();
      void ar(unsigned portNum, sample_t *buffer, jack_nframes_t len, Options options);
};

#endif
